/**
 * @module path
 * @description
 * 这个模块提供了与路径生成和处理相关的函数。
 * 主要用于创建平滑的曲线路径（Catmull-Rom样条），
 * 以及计算沿路径放置对象时所需的位置和旋转变换。
 */

import TerrainSchematic from "../schematic";
import type { SchematicBlock } from "../types";

/**
 * Interface for the position and rotation of a stamp.
 */
export interface StampTransform {
  position: GameVector3;
  rotation: GameVector3;
}

/**
 * Calculates a point on a Catmull-Rom spline.
 * @param p0, p1, p2, p3 The four control points.
 * @param t The interpolation factor (0 to 1).
 * @returns The interpolated point.
 */
function getPointOnCatmullRom(
  p0: GameVector3,
  p1: GameVector3,
  p2: GameVector3,
  p3: GameVector3,
  t: number
): GameVector3 {
  const t2 = t * t;
  const t3 = t2 * t;

  const x =
    0.5 *
    (2 * p1.x +
      (-p0.x + p2.x) * t +
      (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 +
      (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3);
  const y =
    0.5 *
    (2 * p1.y +
      (-p0.y + p2.y) * t +
      (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 +
      (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
  const z =
    0.5 *
    (2 * p1.z +
      (-p0.z + p2.z) * t +
      (2 * p0.z - 5 * p1.z + 4 * p2.z - p3.z) * t2 +
      (-p0.z + 3 * p1.z - 3 * p2.z + p3.z) * t3);

  return new GameVector3(x, y, z);
}

/**
 * Generates a smooth path using Catmull-Rom splines.
 * @param controlPoints The points the path should pass through.
 * @param pointDensity The number of points to generate between each control point.
 * @returns An array of points representing the smooth path.
 */
export function generateCatmullRomPath(
  controlPoints: GameVector3[],
  pointDensity: number = 20
): GameVector3[] {
  if (controlPoints.length < 2) {
    return controlPoints;
  }

  const pathPoints: GameVector3[] = [];
  // Add virtual points at ends to define the curve at the start and end of the path.
  const points = [
    controlPoints[0],
    ...controlPoints,
    controlPoints[controlPoints.length - 1],
  ];

  for (let i = 1; i < points.length - 2; i++) {
    const p0 = points[i - 1];
    const p1 = points[i];
    const p2 = points[i + 1];
    const p3 = points[i + 2];

    for (let j = 0; j < pointDensity; j++) {
      const t = j / pointDensity;
      pathPoints.push(getPointOnCatmullRom(p0, p1, p2, p3, t));
    }
  }
  // Add the very last point.
  pathPoints.push(points[points.length - 2]);

  return pathPoints;
}

/**
 * Generates transforms (position and rotation) for stamps along a given path.
 * @param path The array of points defining the path.
 * @param step The minimum distance between stamps.
 * @param alignToPath Whether to align the stamps to the path direction.
 * @returns An array of StampTransform objects.
 */
export function generateStampTransformsOnPath(
  path: GameVector3[],
  step: number,
  alignToPath: boolean
): StampTransform[] {
  const transforms: StampTransform[] = [];
  if (path.length < 1) {
    return transforms;
  }

  // Place the first stamp
  let lastStampPosition = path[0];
  let rotation = new GameVector3(0, 0, 0);
  if (alignToPath && path.length > 1) {
    const direction = path[1].clone().sub(path[0]).normalize();
    const yaw = Math.atan2(direction.x, direction.z) * (180 / Math.PI);
    rotation = new GameVector3(0, yaw, 0);
  }
  transforms.push({ position: path[0], rotation });

  for (let i = 1; i < path.length - 1; i++) {
    const currentPoint = path[i];
    const dist = currentPoint.clone().sub(lastStampPosition).mag();

    if (dist >= step) {
      if (alignToPath) {
        const nextPoint = path[i + 1];
        const direction = nextPoint.clone().sub(currentPoint).normalize();
        const yaw = Math.atan2(direction.x, direction.z) * (180 / Math.PI);
        rotation = new GameVector3(0, yaw, 0);
      }
      transforms.push({ position: currentPoint, rotation });
      lastStampPosition = currentPoint;
    }
  }
  return transforms;
}
